package com.enlorenz.common.util;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;

/**
 * URL基础工具类
 * 涉及到网页的读取等操作
 * @author lcb
 */
public class UrlUtils {
	//URL参数链接符号
	public static final String URL_CONNECT_SYMBOL="&";
	//URL数据连接符号
	public static final String URL_PARAM_CONNECT_SYMBOL="=";
	//URL数据带参符号
	public static final String URL_PARAM_TAIL_SYMBOL="?";
	
	/**
	 * 创建参数字符串
	 * @param params 传入map封装的参数值对
	 * @return url参数字符串
	 */
	public static String createParamsString(Map<String,Object> params){
		return createParamsString(params, null);
	}
	
	/**
	 * 创建参数字符串
	 * @param params 传入map封装的参数值对
	 * @param encoding 编码
	 * @return url参数字符串
	 */
	public static String createParamsString(Map<String,Object> params,String encoding){
		if(DataDetUtils.isMapEmpty(params)){
			return "";
		}
		//估算Builder的初始化大小为每个键值对20个字符长度
		int buffInitSize=params.size()*20;
		StringBuilder paramsBuffer=new StringBuilder(buffInitSize);
		int index=0;
	   	for(Object key:params.keySet()){
	   		Object value=params.get(key);
	   		paramsBuffer.append(key);
	   		paramsBuffer.append(URL_PARAM_CONNECT_SYMBOL);
	   		if(null != value){
	   			if(!DataDetUtils.isStrEmpty(encoding)){
	   				value=encodeParam(String.valueOf(value), encoding);
	   			}
	   			paramsBuffer.append(value);
	   		}
	   		
	   		index++;
	   		if(params.size() != index){
	   			paramsBuffer.append(URL_CONNECT_SYMBOL);
	   		}
	   	}
	   	return paramsBuffer.toString();
	}
	
	/**
	 * 组合url链接和参数
	 * @param url	url请求地址
	 * @param param	参数字符串
	 * @return	带参的url链接
	 */
	public static String connectUrlParam(String url,String param){
		//如果以?结尾则直接加入参数
		if(url.endsWith(URL_PARAM_TAIL_SYMBOL)){
			url=url+param;
		//如果包含?则加入连接符
		}else if(-1!=url.indexOf(URL_PARAM_TAIL_SYMBOL)){
			url=url+URL_CONNECT_SYMBOL+param;
		}else{
			url=url+URL_PARAM_TAIL_SYMBOL+param;
		}
		return url;
	}
	
	/**
	 * 组合url链接和参数
	 * @param url url请求地址
	 * @param param 参数map数据
	 * @return 带参的url链接
	 */
	public static String connectUrlParam(String url,Map<String,Object> params){
		return connectUrlParam(url, createParamsString(params));
	}
	
	/**
	 * 组合url链接和参数
	 * 直接用反射读取对象的域值
	 * @param url url请求地址
	 * @param object 对象
	 * @return 带参的url链接
	 */
	public static String connectUrlParamFromObject(String url,Object object){
		Map map=MapUtils.transObject2Map(object);
		return connectUrlParam(url, createParamsString(map));
	}
	
	
	/**
	 * 读取然后写入到文件
	 * @param urlStr 链接地址
	 * @param encode 字符编码
	 * @param filePath 文件路径
	 * @throws Exception
	 */
	public static void readUrlAndWrite2File(String urlStr,String encode,String filePath) throws Exception{
		String html=readUrl(urlStr, encode);
		FileUtils.writeFile(filePath, html, encode);
	}

	/**
	 * 读取一个链接写出到文件中
	 * @param urlStr 链接地址
	 * @param filePath 文件路径
	 * @throws Exception
	 */
	public static void readUrlAndWrite2File(String urlStr,String filePath) throws Exception{
		byte [] data=readBytes(urlStr);
		FileUtils.writeFile(filePath, data);
	}
	
	/**
	 * 编码参数
	 * @param param 参数
	 * @param encoding 编码
	 * @return 编码后的数据
	 */
	public static String encodeParam(String param,String encoding){
		try {
			return java.net.URLEncoder.encode(param,encoding);
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * 读取Url数据
	 * @param urlStr url路径
	 * @param encode 字符编码
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 读取的url数据
	 * @throws Exception
	 */
	public static String readUrl(String urlStr,String encode,int connectTimeout,int readTimeout){
		byte [] buff=readBytes(urlStr,connectTimeout,readTimeout);
		if(null == buff){
			return null;
		}
		return StringUtils.newString(buff, encode);
	}
	
	//默认的url链接超时
	public static int DEFAULT_URL_CONNECT_TIMEOUT=5000;
	//默认的url读取超时
	public static int DEFAULT_URL_READ_TIMEOUT=5000;
	
	/**
	 * 读取Url数据
	 * @param urlStr url路径
	 * @param params 参数
	 * @param encode 字符编码
	 * @return 读取的url数据
	 * @throws Exception
	 */
	public static String readUrl(String urlStr,Map params,String encode){
		String req=connectUrlParam(urlStr, createParamsString(params,encode));
		return readUrl(req, encode);
	}
	
	/**
	 * 读取Url数据
	 * @param urlStr url路径
	 * @param encode 字符编码
	 * @return 读取的url数据
	 * @throws Exception
	 */
	public static String readUrl(String urlStr,String encode){
		byte [] buff=readBytes(urlStr,DEFAULT_URL_CONNECT_TIMEOUT,DEFAULT_URL_READ_TIMEOUT);
		if(null == buff){
			return null;
		}
		return StringUtils.newString(buff, encode);
	}
	
	/**
	 * 读取Bytes数据
	 * @param urlStr url路径
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 读取的url数据
	 * @throws Exception
	 */
	public static byte [] readBytes(String urlStr){
		return readBytes(urlStr, DEFAULT_URL_CONNECT_TIMEOUT, DEFAULT_URL_READ_TIMEOUT);
	}
	
	/**
	 * 读取Bytes数据
	 * @param urlStr url路径
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 读取的url数据
	 * @throws Exception
	 */
	public static byte [] readBytes(String urlStr,int connectTimeout,int readTimeout){
		InputStream is = null;
		HttpURLConnection uc =null;
		byte [] commBuffer=null;
		URL url = null;
		try {
			url=new URL(urlStr);
			uc=(HttpURLConnection)url.openConnection();
			uc.setConnectTimeout(connectTimeout);
			uc.setReadTimeout(readTimeout);
			is = uc.getInputStream();
			commBuffer=StreamUtils.readStreamOnce(is);
		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			closeHttpURLConnection(uc);
		}
		
		return commBuffer;
	}
	
	/**
	 * 关闭Http链接
	 * @param uc url链接
	 */
	public static void closeHttpURLConnection(HttpURLConnection uc){
		try {
			if (null != uc) {
				uc.disconnect();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 进行http post请求
	 * @param url 请求地址
	 * @param params 参数
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 请求结果
	 */
	@SuppressWarnings("rawtypes")
	public static String httpPost(String url, Map params
			,int connectTimeout,int readTimeout){
		return httpPost(url, params, CommonContants.UTF_8_CHARSET,connectTimeout,readTimeout);
	}
	
	public static final String REQUEST_METHOD_POST="POST";
	public static final String REQUEST_METHOD_GET="GET";
	
	/**
	 * 进行http post请求
	 * @param url 请求地址
	 * @param params 参数
	 * @param encoding 字符编码
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 请求结果
	 */
	@SuppressWarnings("rawtypes")
	public static String httpPost(String url, Map params,String encoding
			,int connectTimeout,int readTimeout){
		return http(REQUEST_METHOD_POST, url, params, encoding, connectTimeout, readTimeout);
	}
	
	/**
	 * 进行http get请求
	 * @param url 请求地址
	 * @param params 参数
	 * @param encoding 字符编码
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 请求结果
	 */
	@SuppressWarnings("rawtypes")
	public static String httpGet(String url, Map params,String encoding
			,int connectTimeout,int readTimeout){
		return http(REQUEST_METHOD_GET, url, params, encoding, connectTimeout, readTimeout);
	}
	
	
	/**
	 * 进行http
	 * @param requestMethod 请求类型
	 * @param url 请求地址
	 * @param params 参数
	 * @param encoding 字符编码
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 请求结果
	 */
	@SuppressWarnings("rawtypes")
	public static String http(String requestMethod,String url, Map params,String encoding
			,int connectTimeout,int readTimeout){
		HttpURLConnection con = null;
		OutputStream outputStream=null;
		InputStream inputStream=null;
		// 尝试发送请求
		try {
			URL u = new URL(url);
			con = (HttpURLConnection) u.openConnection();
			con.setRequestMethod(requestMethod);
			con.setDoOutput(true);
			con.setDoInput(true);
			con.setUseCaches(false);
			con.setConnectTimeout(connectTimeout);
			con.setReadTimeout(readTimeout);
			con.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
			
			outputStream=con.getOutputStream();
			
			//将map拼接成url请求参数
			String paramsStr=createParamsString(params);
			byte [] sendBytes=paramsStr.getBytes(encoding);
			StreamUtils.writeStreamOnceIfClose(outputStream,sendBytes,false);
		} catch (Exception e) {
			StreamUtils.closeStream(outputStream);
			closeHttpURLConnection(con);
			throw new RuntimeException(e);
		}finally{
			StreamUtils.closeStream(outputStream);
		}

		// 读取返回内容
		String msg=null;
		try {
			inputStream=con.getInputStream();
			byte [] readData = StreamUtils.readStreamOnceIfClose(inputStream,false);
			if(!DataDetUtils.isByteArrEmpty(readData)){
				msg=new String(readData,encoding);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}finally {
			StreamUtils.closeStream(inputStream);
			closeHttpURLConnection(con);
		}
		return msg;
	}

	@SuppressWarnings("rawtypes")
	public static String httpPostString(String url, String param,String encoding
			,int connectTimeout,int readTimeout){
		return httpString(REQUEST_METHOD_POST, url, param, encoding, connectTimeout, readTimeout);
	}
	
	
	/**
	 * 进行http
	 * @param requestMethod 请求类型
	 * @param url 请求地址
	 * @param params 参数
	 * @param encoding 字符编码
	 * @param connectTimeout 链接超时
	 * @param readTimeout 读取超时
	 * @return 请求结果
	 */
	@SuppressWarnings("rawtypes")
	public static String httpString(String requestMethod,String url, String param,String encoding
			,int connectTimeout,int readTimeout){
		HttpURLConnection con = null;
		OutputStream outputStream=null;
		InputStream inputStream=null;
		// 尝试发送请求
		try {
			URL u = new URL(url);
			con = (HttpURLConnection) u.openConnection();
			con.setRequestMethod(requestMethod);
			con.setDoOutput(true);
			con.setDoInput(true);
			con.setUseCaches(false);
			con.setConnectTimeout(connectTimeout);
			con.setReadTimeout(readTimeout);
			con.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
			
			outputStream=con.getOutputStream();
			
			byte [] sendBytes=param.getBytes(encoding);
			StreamUtils.writeStreamOnceIfClose(outputStream,sendBytes,false);
		} catch (Exception e) {
			StreamUtils.closeStream(outputStream);
			closeHttpURLConnection(con);
			throw new RuntimeException(e);
		}finally{
			StreamUtils.closeStream(outputStream);
		}

		// 读取返回内容
		String msg=null;
		try {
			inputStream=con.getInputStream();
			byte [] readData = StreamUtils.readStreamOnceIfClose(inputStream,false);
			if(!DataDetUtils.isByteArrEmpty(readData)){
				msg=new String(readData,encoding);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}finally {
			StreamUtils.closeStream(inputStream);
			closeHttpURLConnection(con);
		}
		return msg;
	}
	
}
